package cn.cycleif.app.service;


import cn.cycleif.app.domain.dto.QuestionAddDTO;
import cn.cycleif.app.domain.vo.QuestionVO;
import cn.cycleif.app.mapper.*;
import cn.cycleif.aspect.HyperLogLimit;
import cn.cycleif.entity.*;
import cn.cycleif.enums.CommonStatusEnum;
import cn.cycleif.enums.ObjectTypeEnum;
import cn.cycleif.enums.ResultCodeEnum;
import cn.cycleif.enums.VoteStatusEnum;
import cn.cycleif.util.PageUtil;
import cn.cycleif.util.R;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * @Date: 25/2/6-18:41
 * @Desc:
 */
@Service
@RequiredArgsConstructor
public class QuestionService extends ServiceImpl<QuestionMapper, Question> {


  private final QuestionMapper    questionMapper;
  private final TagQuestionMapper tagQuestionMapper;
  private final AnswerMapper      answerMapper;
  private final TagMapper         tagMapper;
  private final UserMapper        userMapper;
  private final UserService       userService;
  private final RedisService      redisService;
  private final RevisionService   revisionService;

  public R<Object> getPage(String order, String tagName) {
    Page<Question>               page    = new Page<>(PageUtil.getPageNo(), PageUtil.getPageSize());
    LambdaQueryWrapper<Question> wrapper = new LambdaQueryWrapper<>();
    switch (order) {
      case "hot":
      case "mostAnswer":
        wrapper.orderByDesc(Question::getAnswerCount);
        break;
      case "unsolved":
        wrapper.isNull(Question::getAcceptedAnswerId);
        break;
      case "solved":
        wrapper.isNotNull(Question::getAcceptedAnswerId);
        break;
      case "mostView":
        wrapper.orderByDesc(Question::getViewCount);
        break;
      case "mostLike":
        wrapper.orderByDesc(Question::getVoteCount);
        break;
      case "newest":
      default:
        wrapper.orderByDesc(Question::getCreateAt);
        break;
    }
    Page<Question> page1;
    if (StringUtils.isEmpty(tagName)) { // 不需要按标签过滤
      wrapper.eq(Question::getStatus, CommonStatusEnum.ACTIVE);
      // 排除字段
      wrapper.select(Question.class, v -> !v.getColumn().equals("content"));
      page1 = questionMapper.selectPage(page, wrapper);
    } else {
      LambdaQueryWrapper<Tag> wrapper1 = new LambdaQueryWrapper<>();
      wrapper1.eq(Tag::getSlugName, tagName);
      Tag tag = tagMapper.selectOne(wrapper1);
      if (tag == null) { // 不需要按标签过滤
        wrapper.eq(Question::getStatus, CommonStatusEnum.ACTIVE);
        wrapper.select(Question.class,v -> !v.getColumn().equals("content"));
        page1 = questionMapper.selectPage(page, wrapper);
      } else if (order.equals("solved") || order.equals("unsolved")) {
        page1 = tagQuestionMapper.pageQueryWithCondition(page, tag.getId(), wrapper);
      } else {
        page1 = tagQuestionMapper.pageQueryWithOrder(page, tag.getId(), wrapper);
      }
    }
    // 修改内容
    Page<QuestionVO> voPage = new Page<>();
    voPage.setCurrent(page1.getCurrent());
    voPage.setSize(page1.getSize());
    voPage.setTotal(page1.getTotal());
    voPage.setPages(page1.getPages());
    // 添加其他信息
    List<Question>   records    = page1.getRecords();
    List<QuestionVO> sysUserVOS = records.stream().map(this::appendExtraInfo).toList();
    voPage.setRecords(sysUserVOS);
    return R.success(voPage);
  }

  @Transactional(rollbackFor = Exception.class)
  public R<Integer> addQuestion(QuestionAddDTO dto, boolean withAnswer) {
    int userId = StpUtil.getLoginIdAsInt();
    Question question = Question.builder().title(dto.getTitle()).content(dto.getContent()).userId(userId)
                                .lastEditUserId(userId).status(CommonStatusEnum.ACTIVE).build();
    if (withAnswer) {
      question.setAnswerCount(1);
    }
    questionMapper.insert(question);
    Answer answer = new Answer();
    if (withAnswer) {
      answer.setQuestionId(question.getId());
      answer.setContent(dto.getAnswer());
      answer.setUserId(userId);
      answer.setStatus(CommonStatusEnum.ACTIVE);
      answer.setAdopted(1);
      answer.setLastEditUserId(userId);
      answerMapper.insert(answer);
      question.setAcceptedAnswerId(answer.getId());
      question.setLastAnswerId(answer.getId());
      questionMapper.updateById(question);
    }
    // 处理标签
    for (Integer tagId : dto.getTagIds()) {
      if (tagId == null) continue;
      TagQuestion tagQuestion = new TagQuestion();
      tagQuestion.setTagId(tagId);
      tagQuestion.setQuestionId(question.getId());
      tagQuestionMapper.insert(tagQuestion);
      // 更新 tag
      LambdaUpdateWrapper<Tag> wrapper = new LambdaUpdateWrapper<>();
      wrapper.setSql("question_count = question_count + 1");
      wrapper.eq(Tag::getId, tagId);
      tagMapper.update(wrapper);
    }
    //
    LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
    wrapper.setSql("question_count = question_count + 1");
    wrapper.eq(User::getId, userId);
    userMapper.update(wrapper);
    // 记录日志
    revisionService.add(ObjectTypeEnum.QUESTION, question.getId(), dto.getTitle(), dto.getContent());
    return R.success(question.getId());
  }

  @HyperLogLimit
  public R<QuestionVO> getQuestion(Integer id) {
    Question question = questionMapper.selectById(id);
    if (question == null) return R.error(ResultCodeEnum.ERROR_NOT_EXIST);
    return R.success(appendExtraInfo(question));
  }

  /**
   * 添加user和tag信息
   *
   * @param question
   * @return
   */
  private QuestionVO appendExtraInfo(Question question) {
    QuestionVO              vo       = BeanUtil.copyProperties(question, QuestionVO.class);
    HashMap<String, Object> userInfo = userService.getUserSimpleInfo(question.getUserId(), null);
    vo.setUserInfo(userInfo);
    List<Object> tags = getQuestionTagsInfo(question.getId());
    vo.setTags(tags);
    // 查询redis点赞数及阅读数，空的则不需要，否则说明数据未落库，需要查询(这里已经查询了)
    Integer viewCount = redisService.getQuestionViewCount(question.getId());
    if (viewCount != null) vo.setViewCount(viewCount + question.getViewCount());
    Integer voteCnt = redisService.getVoteCnt(ObjectTypeEnum.QUESTION, question.getId());
    if (voteCnt != null) vo.setVoteCount(voteCnt + question.getVoteCount());
    if (StpUtil.isLogin()) {
      int userId = StpUtil.getLoginIdAsInt();
      VoteStatusEnum status =
          redisService.getVoteStatus(ObjectTypeEnum.QUESTION, question.getId(), userId);
      vo.setVoteStatus(status);
    }
    return vo;
  }

  /**
   * 获取问题的标签精简信息
   *
   * @param id
   * @return
   */
  @NotNull
  public List<Object> getQuestionTagsInfo(Integer id) {
    List<Object>              tags     = new ArrayList<>();
    QueryWrapper<TagQuestion> wrapper1 = new QueryWrapper<>();
    wrapper1.eq("question_id", id);
    List<TagQuestion> tagQuestions = tagQuestionMapper.selectList(wrapper1);
    for (TagQuestion tagQuestion : tagQuestions) {
      Tag tag = tagMapper.selectById(tagQuestion.getTagId());

      HashMap<String, Object> map = new HashMap<>();
      map.put("id", tag.getId());
      map.put("slugName", tag.getSlugName());
      map.put("displayName", tag.getDisplayName());
      tags.add(map);
    }
    return tags;
  }


  @Transactional
  public R<Integer> deleteQuestion(Integer id) {
    Question question = questionMapper.selectById(id);
    if (question == null) return R.error(ResultCodeEnum.ERROR_NOT_EXIST);
    // 检查外键（伪）
    deleteQuestionTagsRel(id);
    question.setStatus(CommonStatusEnum.DELETE);
    questionMapper.updateById(question);

    revisionService.delete(ObjectTypeEnum.QUESTION, id, null);
    return R.success();
  }

  private void deleteQuestionTagsRel(Integer id) {
    LambdaQueryWrapper<TagQuestion> wrapper = new LambdaQueryWrapper<>();
    wrapper.eq(TagQuestion::getQuestionId, id);
    // 设置其他表
    List<TagQuestion> tagQuestions = tagQuestionMapper.selectList(wrapper);
    for (TagQuestion tagQuestion : tagQuestions) {
      tagQuestionMapper.deleteById(tagQuestion.getId()); // 删除中间表
      LambdaUpdateWrapper<Tag> wrapper1 = new LambdaUpdateWrapper<>();
      wrapper1.eq(Tag::getId, tagQuestion.getTagId());
      wrapper1.setSql("question_count=question_count-1");
      tagMapper.update(wrapper1);
    }
  }

  @Transactional(rollbackFor = Exception.class)
  public R<Boolean> editQuestion(Integer id, QuestionAddDTO dto) {

    Question question = questionMapper.selectById(id);
    if (question == null) return R.error(ResultCodeEnum.ERROR_NOT_EXIST);

    int userId = StpUtil.getLoginIdAsInt();
    question.setTitle(dto.getTitle());
    question.setContent(dto.getContent());
    question.setLastEditUserId(userId);

    questionMapper.updateById(question);
    // 更新标签(先删除，再添加)
    deleteQuestionTagsRel(id);

    List<Integer> tagIds = dto.getTagIds();
    if (tagIds != null && tagIds.size() > 0) {
      for (Integer tagId : tagIds) {
        Tag tag = tagMapper.selectById(tagId);
        if (tag == null) continue;
        LambdaUpdateWrapper<Tag> wrapper1 = new LambdaUpdateWrapper<>();
        wrapper1.eq(Tag::getId, tagId);
        wrapper1.setSql("question_count=question_count+1");
        tagMapper.update(wrapper1);

        LambdaQueryWrapper<TagQuestion> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(TagQuestion::getQuestionId, id);
        wrapper.eq(TagQuestion::getTagId, tagId);


        TagQuestion tagQuestion = new TagQuestion();
        tagQuestion.setQuestionId(id);
        tagQuestion.setTagId(tagId);
        tagQuestionMapper.insert(tagQuestion);
      }
    }

    revisionService.update(ObjectTypeEnum.QUESTION, id, dto.getTitle(), dto.getContent(), null);
    return R.success();
  }
}
